// Includes
#include "Texture.h"

#ifdef _WIN32
#include <windows.h>		// Include windows header on the windows platform
#endif
#include <GL/gl.h>		// OpenGL header
#include <GL/glu.h>		// OpenGL Utility header
#include <cstring>		// C string library header

namespace EraAstral
{
	namespace graphics
	{
		// Texture class constructor
		Texture::Texture()
		{
			// Set null pointers
			m_data = NULL;
						
			// Set 0 dimensions
			m_width = m_height = 0;
			
			// Standard format: Unsigned bytes
			m_format = GL_UNSIGNED_BYTE;
			m_hasAlpha = false;
			
			// Generate an OpenGL texture
			glGenTextures(1, &m_glIndex);
		}
		
		// Constructor creating a texture from an SDL surface
		Texture::Texture(const SDL_Surface *surface)
		{
			// Get dimensions
			m_width = surface->w;
			m_height = surface->h;
			
			// Size in bytes of pixel data
			int numPixels = m_width * m_height;
			size_t bytes = numPixels * surface->format->BytesPerPixel;
			
			// Allocate memory for pixel data
			m_data = new char[bytes];
			// Copy pixel data
			memcpy(m_data, surface->pixels, bytes);
			
			// Assume alpha value
			m_hasAlpha = true;
			
			// Determine format
			switch (surface->format->BitsPerPixel)
			{
				case 24:
					// 24 bits - No alpha
					m_hasAlpha = false;
				case 32:
					m_format = GL_UNSIGNED_BYTE;
					break;
				// Unknown component size
				default:
					m_hasAlpha = false;
					m_format = GL_UNSIGNED_BYTE;
					break;
			}
			
			// Generate an OpenGL texture
			glGenTextures(1, &m_glIndex);
		}
		
		// Texture class destructor
		Texture::~Texture()
		{
			// Free pixel data memory
			if (m_data != NULL)
			{
				switch (m_format)
				{
					// Unsigned byte format
					case GL_UNSIGNED_BYTE:
						delete[] (unsigned char *)m_data;
						break;
					// Unknown pixel format
					default:
						break;
				}
				
				m_data = NULL;
			}
			
			// Delete OpenGL texture
			glDeleteTextures(1, &m_glIndex);
		}
		
		// Commits the texture changes to OpenGL
		GLuint Texture::Commit()
		{			
			// Bind current texture
			glBindTexture(GL_TEXTURE_2D, m_glIndex);
			
			// Internal format of the texture
			GLint internalFormat;
			if (m_hasAlpha)
				internalFormat = GL_RGBA;
			else
				internalFormat = GL_RGB;
			
			// Create texture
			glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, m_width, m_height, 0, internalFormat, m_format, m_data);
			
			// Modulate texture with color
			glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);			
			
			// Texture filtering
			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);		// Min filter: Bilinear with closest mip
			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);						// Mag filter: Bilinear filtering
			
			// Texture wrapping
			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);			// Repeating texture wrapping
			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
			
			// Create mipmaps from data
			gluBuild2DMipmaps(GL_TEXTURE_2D, internalFormat, m_width, m_height,
					internalFormat, m_format, m_data);
		
			return 0;
		}
		
		// Binds the texture
		void Texture::Bind()
		{
			glBindTexture(GL_TEXTURE_2D, m_glIndex);
		}
		
		// Get pointer to pixel data
		void *Texture::GetPixelData() const
		{
			return m_data;
		}
		
		// Get texture width
		unsigned int Texture::GetWidth()
		{
			return m_width;
		}
		
		// Get texture height
		unsigned int Texture::GetHeight()
		{
			return m_height;
		}
		
		// Get pixel format of the texture
		GLenum Texture::GetFormat()
		{
			return m_format;
		}
	}
}
